repo: Introduce ostree_repo_open_at() and ostree_repo_create_at()
authorColin Walters <walters@verbum.org>
Fri, 28 Apr 2017 19:51:32 +0000 (15:51 -0400)
committerAtomic Bot <atomic-devel@projectatomic.io>
Tue, 15 Aug 2017 12:35:10 +0000 (12:35 +0000)
This essentially completes our fd-relative conversion.

While here, I cleaned up the semantics of `ostree_repo_create()` and
`ostree_repo_create_at()` to be more atomic - basically various scripts were
testing for the `objects` subdirectory, so let's formalize that.

Closes: #820
Approved by: jlebon

apidoc/ostree-sections.txt
src/libostree/libostree-devel.sym
src/libostree/ostree-repo-finder-mount.c
src/libostree/ostree-repo-private.h
src/libostree/ostree-repo.c
src/libostree/ostree-repo.h
src/libostree/ostree-sysroot-private.h
src/libostree/ostree-sysroot.c
tests/test-repo-finder-config.c
tests/test-repo-finder-mount.c

index df9767d4ff6d42b54ebd1f831f5968b0df192372..90bec1672049df9396e804d830e98d81f3affac4 100644 (file)
@@ -271,6 +271,7 @@ ostree_mutable_tree_get_type
 OstreeRepo
 OstreeRepoMode
 ostree_repo_mode_from_string
+ostree_repo_open_at
 ostree_repo_new
 ostree_repo_new_for_sysroot_path
 ostree_repo_new_default
@@ -279,6 +280,7 @@ ostree_repo_set_disable_fsync
 ostree_repo_get_disable_fsync
 ostree_repo_is_system
 ostree_repo_is_writable
+ostree_repo_create_at
 ostree_repo_create
 ostree_repo_get_path
 ostree_repo_get_mode
index 49111b4aa53ff8297fd852786f54fc8192e51a06..82b18667747667cb2b52eea671fb7a4204ce786c 100644 (file)
@@ -21,6 +21,8 @@
 LIBOSTREE_2017.10 {
   ostree_gpg_error_quark;
   ostree_repo_set_alias_ref_immediate;
+  ostree_repo_open_at;
+  ostree_repo_create_at;
 };
 
 /* Stub section for the stable release *after* this development one; don't
index 1eb3d31d045ba2d447c7149e531d913de5294e52..7f257f689548968c1e6df42bf52084693751ae17 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "ostree-autocleanups.h"
 #include "ostree-remote-private.h"
+#include "ostree-repo-private.h"
 #include "ostree-repo-finder.h"
 #include "ostree-repo-finder-mount.h"
 
@@ -257,19 +258,16 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder                  *finde
         {
           struct stat stbuf;
           g_autofree gchar *collection_and_ref = NULL;
-          g_autofree gchar *repo_dir_path = NULL;
           g_autofree gchar *resolved_repo_uri = NULL;
           g_autofree gchar *keyring = NULL;
           g_autoptr(UriAndKeyring) resolved_repo = NULL;
 
           collection_and_ref = g_build_filename (refs[i]->collection_id, refs[i]->ref_name, NULL);
-          repo_dir_path = g_build_filename (mount_root_path, ".ostree", "repos",
-                                            collection_and_ref, NULL);
 
           if (!glnx_fstatat (repos_dfd, collection_and_ref, &stbuf, AT_NO_AUTOMOUNT, &local_error))
             {
               g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as querying info of ‘%s’ failed: %s",
-                       refs[i]->collection_id, refs[i]->ref_name, mount_name, repo_dir_path, local_error->message);
+                       refs[i]->collection_id, refs[i]->ref_name, mount_name, collection_and_ref, local_error->message);
               g_clear_error (&local_error);
               continue;
             }
@@ -277,7 +275,7 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder                  *finde
           if ((stbuf.st_mode & S_IFMT) != S_IFDIR)
             {
               g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as ‘%s’ is of type %u, not a directory.",
-                       refs[i]->collection_id, refs[i]->ref_name, mount_name, repo_dir_path, (stbuf.st_mode & S_IFMT));
+                       refs[i]->collection_id, refs[i]->ref_name, mount_name, collection_and_ref, (stbuf.st_mode & S_IFMT));
               g_clear_error (&local_error);
               continue;
             }
@@ -294,27 +292,19 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder                  *finde
             }
 
           /* Exclude repositories which resolve to @parent_repo. */
-          g_autofree char *canonical_repo_dir_path = realpath (repo_dir_path, NULL);
-          g_autofree gchar *parent_repo_path = g_file_get_path (ostree_repo_get_path (parent_repo));
-          g_autofree char *canonical_parent_repo_path = realpath (parent_repo_path, NULL);
-
-          if (g_strcmp0 (canonical_repo_dir_path, canonical_parent_repo_path) == 0)
+          if (stbuf.st_dev == parent_repo->device &&
+              stbuf.st_ino == parent_repo->inode)
             {
-              g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository was the one we are resolving for: %s",
-                       refs[i]->collection_id, refs[i]->ref_name, mount_name, canonical_parent_repo_path);
+              g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as it is the same as the one we are resolving",
+                       refs[i]->collection_id, refs[i]->ref_name, mount_name);
               g_clear_error (&local_error);
               continue;
             }
 
-          /* Grab the given ref and a checksum for it from the repo.
-           * FIXME: Ideally, there would be some ostree_repo_open_at() which we
-           * could use to keep the openat() chain going. See
-           * https://github.com/ostreedev/ostree/pull/820. */
-          g_autoptr(OstreeRepo) repo = NULL;
-          g_autoptr(GFile) repo_dir_file = g_file_new_for_path (repo_dir_path);
-          repo = ostree_repo_new (repo_dir_file);
-
-          if (!ostree_repo_open (repo, cancellable, &local_error))
+          /* Grab the given ref and a checksum for it from the repo, if it appears to be a valid repo */
+          g_autoptr(OstreeRepo) repo = ostree_repo_open_at (repos_dfd, collection_and_ref,
+                                                            cancellable, &local_error);
+          if (!repo)
             {
               g_debug ("Ignoring ref (%s, %s) on mount ‘%s’ as its repository could not be opened: %s",
                        refs[i]->collection_id, refs[i]->ref_name, mount_name, local_error->message);
@@ -358,6 +348,11 @@ ostree_repo_finder_mount_resolve_async (OstreeRepoFinder                  *finde
            * $mount_root/.ostree/repos/$refs[i]->collection_id/$refs[i]->ref_name.
            * Add it to the results, keyed by the canonicalised repository URI
            * to deduplicate the results. */
+
+          g_autofree char *repo_abspath = g_build_filename (mount_root_path, ".ostree/repos",
+                                                            collection_and_ref, NULL);
+          /* FIXME - why are we using realpath here? */
+          g_autofree char *canonical_repo_dir_path = realpath (repo_abspath, NULL);
           resolved_repo_uri = g_strconcat ("file://", canonical_repo_dir_path, NULL);
           g_debug ("Resolved ref (%s, %s) on mount ‘%s’ to repo URI ‘%s’ with keyring ‘%s’.",
                    refs[i]->collection_id, refs[i]->ref_name, mount_name, resolved_repo_uri, keyring);
index d2c41316ddbc9ff7fdbb0600adf8d8d30832351a..e38e48bcc197e076e218303fc33cbec53f65f0b8 100644 (file)
@@ -96,7 +96,11 @@ struct OstreeRepo {
   char *commit_stagedir_name;
   GLnxLockFile commit_stagedir_lock;
 
-  GFile *repodir;
+  /* A cached fd-relative version, distinct from the case where we may have a
+   * user-provided absolute path.
+   */
+  GFile *repodir_fdrel;
+  GFile *repodir; /* May be %NULL if we were opened via ostree_repo_open_at() */
   int    repo_dir_fd;
   int    tmp_dir_fd;
   int    cache_dir_fd;
@@ -132,10 +136,13 @@ struct OstreeRepo {
   GHashTable *updated_uncompressed_dirs;
   GHashTable *object_sizes;
 
-  uid_t owner_uid;
-  uid_t target_owner_uid;
+  /* Cache the repo's device/inode to use for comparisons elsewhere */
+  dev_t device;
+  ino_t inode;
+  uid_t owner_uid; /* Cache of repo's owner uid */
+  uid_t target_owner_uid; /* Ensure files are chowned to this uid/gid */
   gid_t target_owner_gid;
-  guint min_free_space_percent;
+  guint min_free_space_percent; /* See the min-free-space-percent config option */
 
   guint test_error_flags; /* OstreeRepoTestErrorFlags */
 
index df019dd63d5dfda5791f3e9186d46fe178baf489..795016ce7f17ff83b9222993a5c491ea3cc28bef 100644 (file)
@@ -456,6 +456,7 @@ ostree_repo_finalize (GObject *object)
   g_clear_object (&self->parent_repo);
 
   g_free (self->stagedir_prefix);
+  g_clear_object (&self->repodir_fdrel);
   g_clear_object (&self->repodir);
   if (self->repo_dir_fd != -1)
     (void) close (self->repo_dir_fd);
@@ -546,22 +547,11 @@ ostree_repo_get_property(GObject         *object,
     }
 }
 
-static void
-ostree_repo_constructed (GObject *object)
-{
-  OstreeRepo *self = OSTREE_REPO (object);
-
-  g_assert (self->repodir != NULL);
-
-  G_OBJECT_CLASS (ostree_repo_parent_class)->constructed (object);
-}
-
 static void
 ostree_repo_class_init (OstreeRepoClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  object_class->constructed = ostree_repo_constructed;
   object_class->get_property = ostree_repo_get_property;
   object_class->set_property = ostree_repo_set_property;
   object_class->finalize = ostree_repo_finalize;
@@ -581,6 +571,7 @@ ostree_repo_class_init (OstreeRepoClass *klass)
                                                         "",
                                                         G_TYPE_FILE,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
   g_object_class_install_property (object_class,
                                    PROP_REMOTES_CONFIG_DIR,
                                    g_param_spec_string ("remotes-config-dir",
@@ -662,6 +653,43 @@ ostree_repo_new (GFile *path)
   return g_object_new (OSTREE_TYPE_REPO, "path", path, NULL);
 }
 
+static OstreeRepo *
+repo_open_at_take_fd (int *dfd,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+  g_autoptr(OstreeRepo) repo = g_object_new (OSTREE_TYPE_REPO, NULL);
+  repo->repo_dir_fd = glnx_steal_fd (dfd);
+
+  if (!ostree_repo_open (repo, cancellable, error))
+    return NULL;
+  return g_steal_pointer (&repo);
+}
+
+/**
+ * ostree_repo_open_at:
+ * @dfd: Directory fd
+ * @path: Path
+ *
+ * This combines ostree_repo_new() (but using fd-relative access) with
+ * ostree_repo_open().  Use this when you know you should be operating on an
+ * already extant repository.  If you want to create one, use ostree_repo_create_at().
+ *
+ * Returns: (transfer full): An accessor object for an OSTree repository located at @dfd + @path
+ */
+OstreeRepo*
+ostree_repo_open_at (int           dfd,
+                     const char   *path,
+                     GCancellable *cancellable,
+                     GError      **error)
+{
+  glnx_fd_close int repo_dfd = -1;
+  if (!glnx_opendirat (dfd, path, TRUE, &repo_dfd, error))
+    return NULL;
+
+  return repo_open_at_take_fd (&repo_dfd, cancellable, error);
+}
+
 static GFile *
 get_default_repo_path (GFile *sysroot_path)
 {
@@ -744,8 +772,16 @@ ostree_repo_is_system (OstreeRepo   *repo)
   if (!repo->sysroot_dir)
     return FALSE;
 
-  g_autoptr(GFile) default_repo_path = get_default_repo_path (repo->sysroot_dir);
-  return g_file_equal (repo->repodir, default_repo_path);
+  /* If we created via ostree_repo_new(), we'll have a repo path.  Compare
+   * it to the sysroot path in that case.
+   */
+  if (repo->repodir)
+    {
+      g_autoptr(GFile) default_repo_path = get_default_repo_path (repo->sysroot_dir);
+      return g_file_equal (repo->repodir, default_repo_path);
+    }
+  /* Otherwise, not a system repo */
+  return FALSE;
 }
 
 /**
@@ -1670,46 +1706,50 @@ ostree_repo_mode_from_string (const char      *mode,
 #define DEFAULT_CONFIG_CONTENTS ("[core]\n" \
                                  "repo_version=1\n")
 
-/**
- * ostree_repo_create:
- * @self: An #OstreeRepo
- * @mode: The mode to store the repository in
- * @cancellable: Cancellable
- * @error: Error
- *
- * Create the underlying structure on disk for the repository, and call
- * ostree_repo_open() on the result, preparing it for use.
-
- * Since version 2016.8, this function will succeed on an existing
- * repository, and finish creating any necessary files in a partially
- * created repository.  However, this function cannot change the mode
- * of an existing repository, and will silently ignore an attempt to
- * do so.
- *
- */
-gboolean
-ostree_repo_create (OstreeRepo     *self,
-                    OstreeRepoMode  mode,
-                    GCancellable   *cancellable,
-                    GError        **error)
+/* Just write the dirs to disk, return a dfd */
+static gboolean
+repo_create_at_internal (int             dfd,
+                         const char     *path,
+                         OstreeRepoMode  mode,
+                         GVariant       *options,
+                         int            *out_dfd,
+                         GCancellable   *cancellable,
+                         GError        **error)
 {
-  const char *repopath = gs_file_get_path_cached (self->repodir);
-  glnx_fd_close int dfd = -1;
-  struct stat stbuf;
-  const char *state_dirs[] = { "objects", "tmp", "extensions", "state",
+   struct stat stbuf;
+  /* We do objects/ last - if it exists we do nothing and exit successfully */
+  const char *state_dirs[] = { "tmp", "extensions", "state",
                                "refs", "refs/heads", "refs/mirrors",
-                               "refs/remotes" };
+                               "refs/remotes", "objects" };
+
+  /* Early return if we have an existing repo */
+  { g_autofree char *objects_path = g_build_filename (path, "objects", NULL);
+
+    if (fstatat (dfd, objects_path, &stbuf, 0) == 0)
+      {
+        glnx_fd_close int repo_dfd = -1;
+        if (!glnx_opendirat (dfd, path, TRUE, &repo_dfd, error))
+          return FALSE;
+
+        /* Note early return */
+        *out_dfd = glnx_steal_fd (&repo_dfd);
+        return TRUE;
+      }
+    else if (errno != ENOENT)
+      return glnx_throw_errno_prefix (error, "fstatat");
+  }
 
-  if (mkdir (repopath, 0755) != 0)
+  if (mkdirat (dfd, path, 0755) != 0)
     {
       if (G_UNLIKELY (errno != EEXIST))
-        return glnx_throw_errno (error);
+        return glnx_throw_errno_prefix (error, "mkdirat");
     }
 
-  if (!glnx_opendirat (AT_FDCWD, repopath, TRUE, &dfd, error))
+  glnx_fd_close int repo_dfd = -1;
+  if (!glnx_opendirat (dfd, path, TRUE, &repo_dfd, error))
     return FALSE;
 
-  if (fstatat (dfd, "config", &stbuf, 0) < 0)
+  if (fstatat (repo_dfd, "config", &stbuf, 0) < 0)
     {
       if (errno == ENOENT)
         {
@@ -1722,25 +1762,28 @@ ostree_repo_create (OstreeRepo     *self,
 
           g_string_append_printf (config_data, "mode=%s\n", mode_str);
 
-          if (self->collection_id != NULL)
-            g_string_append_printf (config_data, "collection-id=%s\n", self->collection_id);
+          const char *collection_id = NULL;
+          if (options)
+            g_variant_lookup (options, "collection-id", "&s", &collection_id);
+          if (collection_id != NULL)
+            g_string_append_printf (config_data, "collection-id=%s\n", collection_id);
 
-          if (!glnx_file_replace_contents_at (dfd, "config",
+          if (!glnx_file_replace_contents_at (repo_dfd, "config",
                                               (guint8*)config_data->str, config_data->len,
                                               0, cancellable, error))
             return FALSE;
         }
       else
-        return glnx_throw_errno (error);
+        return glnx_throw_errno_prefix (error, "fstatat");
     }
 
   for (guint i = 0; i < G_N_ELEMENTS (state_dirs); i++)
     {
       const char *elt = state_dirs[i];
-      if (mkdirat (dfd, elt, 0755) == -1)
+      if (mkdirat (repo_dfd, elt, 0755) == -1)
         {
           if (G_UNLIKELY (errno != EEXIST))
-            return glnx_throw_errno (error);
+            return glnx_throw_errno_prefix (error, "mkdirat");
         }
     }
 
@@ -1751,18 +1794,102 @@ ostree_repo_create (OstreeRepo     *self,
     {
       g_auto(GLnxTmpfile) tmpf = { 0, };
 
-      if (!glnx_open_tmpfile_linkable_at (dfd, ".", O_RDWR|O_CLOEXEC, &tmpf, error))
+      if (!glnx_open_tmpfile_linkable_at (repo_dfd, ".", O_RDWR|O_CLOEXEC, &tmpf, error))
         return FALSE;
       if (!_ostree_write_bareuser_metadata (tmpf.fd, 0, 0, 644, NULL, error))
         return FALSE;
   }
 
+  *out_dfd = glnx_steal_fd (&repo_dfd);
+  return TRUE;
+}
+
+/**
+ * ostree_repo_create:
+ * @self: An #OstreeRepo
+ * @mode: The mode to store the repository in
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Create the underlying structure on disk for the repository, and call
+ * ostree_repo_open() on the result, preparing it for use.
+
+ * Since version 2016.8, this function will succeed on an existing
+ * repository, and finish creating any necessary files in a partially
+ * created repository.  However, this function cannot change the mode
+ * of an existing repository, and will silently ignore an attempt to
+ * do so.
+ *
+ * Since 2017.9, "existing repository" is defined by the existence of an
+ * `objects` subdirectory.
+ *
+ * This function predates ostree_repo_create_at(). It is an error to call
+ * this function on a repository initialized via ostree_repo_open_at().
+ */
+gboolean
+ostree_repo_create (OstreeRepo     *self,
+                    OstreeRepoMode  mode,
+                    GCancellable   *cancellable,
+                    GError        **error)
+{
+  g_return_val_if_fail (self->repodir, FALSE);
+  const char *repopath = gs_file_get_path_cached (self->repodir);
+  g_autoptr(GVariantBuilder) builder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
+  if (self->collection_id)
+    g_variant_builder_add (builder, "{s@v}", "collection-id",
+                           g_variant_new_variant (g_variant_new_string (self->collection_id)));
+
+  glnx_fd_close int repo_dir_fd = -1;
+  if (!repo_create_at_internal (AT_FDCWD, repopath, mode,
+                                g_variant_builder_end (builder),
+                                &repo_dir_fd,
+                                cancellable, error))
+    return FALSE;
+  self->repo_dir_fd = glnx_steal_fd (&repo_dir_fd);
   if (!ostree_repo_open (self, cancellable, error))
     return FALSE;
-
   return TRUE;
 }
 
+/**
+ * ostree_repo_create_at:
+ * @dfd: Directory fd
+ * @path: Path
+ * @mode: The mode to store the repository in
+ * @options: a{sv}: See below for accepted keys
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * This is a file-descriptor relative version of ostree_repo_create().
+ * Create the underlying structure on disk for the repository, and call
+ * ostree_repo_open_at() on the result, preparing it for use.
+ *
+ * If a repository already exists at @dfd + @path (defined by an `objects/`
+ * subdirectory existing), then this function will simply call
+ * ostree_repo_open_at().  In other words, this function cannot be used to change
+ * the mode or configuration (`repo/config`) of an existing repo.
+ *
+ * The @options dict may contain:
+ *
+ *   - collection-id: s: Set as collection ID in repo/config (Since 2017.9)
+ *
+ * Returns: (transfer full): A new OSTree repository reference
+ */
+OstreeRepo *
+ostree_repo_create_at (int             dfd,
+                       const char     *path,
+                       OstreeRepoMode  mode,
+                       GVariant       *options,
+                       GCancellable   *cancellable,
+                       GError        **error)
+{
+  glnx_fd_close int repo_dfd = -1;
+  if (!repo_create_at_internal (dfd, path, mode, options, &repo_dfd,
+                                cancellable, error))
+    return NULL;
+  return repo_open_at_take_fd (&repo_dfd, cancellable, error);
+}
+
 static gboolean
 enumerate_directory_allow_noent (GFile               *dirpath,
                                  const char          *queryargs,
@@ -2132,7 +2259,6 @@ ostree_repo_open (OstreeRepo    *self,
                   GCancellable  *cancellable,
                   GError       **error)
 {
-  struct stat self_stbuf;
   struct stat stbuf;
 
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -2162,22 +2288,25 @@ ostree_repo_open (OstreeRepo    *self,
     self->stagedir_prefix = g_strconcat (OSTREE_REPO_TMPDIR_STAGING, boot_id, "-", NULL);
   }
 
-  if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->repodir), TRUE,
-                       &self->repo_dir_fd, error))
+  if (self->repo_dir_fd == -1)
     {
-      g_prefix_error (error, "%s: ", gs_file_get_path_cached (self->repodir));
-      return FALSE;
+      g_assert (self->repodir);
+      if (!glnx_opendirat (AT_FDCWD, gs_file_get_path_cached (self->repodir), TRUE,
+                           &self->repo_dir_fd, error))
+        {
+          g_prefix_error (error, "%s: ", gs_file_get_path_cached (self->repodir));
+          return FALSE;
+        }
     }
 
-  if (!glnx_fstat (self->repo_dir_fd, &self_stbuf, error))
+  if (!glnx_fstat (self->repo_dir_fd, &stbuf, error))
     return FALSE;
+  self->device = stbuf.st_dev;
+  self->inode = stbuf.st_ino;
 
   if (!glnx_opendirat (self->repo_dir_fd, "objects", TRUE,
                        &self->objects_dir_fd, error))
-    {
-      g_prefix_error (error, "Opening objects/ directory: ");
-      return FALSE;
-    }
+    return FALSE;
 
   self->writable = faccessat (self->objects_dir_fd, ".", W_OK, 0) == 0;
   if (!self->writable)
@@ -2238,8 +2367,8 @@ ostree_repo_open (OstreeRepo    *self,
       if (fstatat (AT_FDCWD, "/ostree/repo", &system_stbuf, 0) == 0)
         {
           /* Are we the same as /ostree/repo? */
-          if (self_stbuf.st_dev == system_stbuf.st_dev &&
-              self_stbuf.st_ino == system_stbuf.st_ino)
+          if (self->device == system_stbuf.st_dev &&
+              self->inode == system_stbuf.st_ino)
             self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_IS_SYSROOT_OSTREE;
           else
             self->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_NO;
@@ -2349,14 +2478,24 @@ _ostree_repo_file_replace_contents (OstreeRepo    *self,
 
 /**
  * ostree_repo_get_path:
- * @self:
+ * @self: Repo
+ *
+ * Note that since the introduction of ostree_repo_open_at(), this function may
+ * return a process-specific path in `/proc` if the repository was created using
+ * that API. In general, you should avoid use of this API.
  *
  * Returns: (transfer none): Path to repo
  */
 GFile *
 ostree_repo_get_path (OstreeRepo  *self)
 {
-  return self->repodir;
+  /* Did we have an abspath?  Return it */
+  if (self->repodir)
+    return self->repodir;
+  /* Lazily create a fd-relative path */
+  if (!self->repodir_fdrel)
+    self->repodir_fdrel = ot_fdrel_to_gfile (self->repo_dir_fd, ".");
+  return self->repodir_fdrel;
 }
 
 /**
index f01fee2abfc48efadcbe6aafb993e5396ec00ddc..8766e6dfcea4edf771a08c0e863e33905d6b1dec 100644 (file)
@@ -63,6 +63,13 @@ gboolean      ostree_repo_open   (OstreeRepo     *self,
                                   GCancellable   *cancellable,
                                   GError        **error);
 
+_OSTREE_PUBLIC
+OstreeRepo*
+ostree_repo_open_at (int           dfd,
+                     const char   *path,
+                     GCancellable *cancellable,
+                     GError      **error);
+
 _OSTREE_PUBLIC
 void          ostree_repo_set_disable_fsync (OstreeRepo    *self,
                                              gboolean       disable_fsync);
@@ -89,6 +96,13 @@ gboolean      ostree_repo_create (OstreeRepo     *self,
                                   OstreeRepoMode  mode,
                                   GCancellable   *cancellable,
                                   GError        **error);
+_OSTREE_PUBLIC
+OstreeRepo *  ostree_repo_create_at (int             dfd,
+                                     const char     *path,
+                                     OstreeRepoMode  mode,
+                                     GVariant       *options,
+                                     GCancellable   *cancellable,
+                                     GError        **error);
 
 #ifdef OSTREE_ENABLE_EXPERIMENTAL_API
 
index 4c6cb32853f6041803dea0e82abab63709385cd5..8c4de9e007bfb430a1ed8320b0c2b3caad0bb02f 100644 (file)
@@ -57,9 +57,8 @@ struct OstreeSysroot {
   OstreeDeployment *booted_deployment;
   struct timespec loaded_ts;
 
-  /* Only access through ostree_sysroot_get_repo() */
+  /* Only access through ostree_sysroot_[_get]repo() */
   OstreeRepo *repo;
-  gboolean repo_opened;
 
   OstreeSysrootDebugFlags debug_flags;
 };
index 5bdfe1167969370f5c40723d19f613e61912ca5d..8d91cde6788f5db69086eb545914fcbfb6833347 100644 (file)
@@ -137,18 +137,11 @@ static void
 ostree_sysroot_constructed (GObject *object)
 {
   OstreeSysroot *self = OSTREE_SYSROOT (object);
-  g_autoptr(GFile) repo_path = NULL;
 
   /* Ensure the system root path is set. */
   if (self->path == NULL)
     self->path = g_object_ref (_ostree_get_default_sysroot_path ());
 
-  repo_path = g_file_resolve_relative_path (self->path, "ostree/repo");
-  self->repo = ostree_repo_new_for_sysroot_path (repo_path, self->path);
-  self->repo->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT;
-  /* Hold a weak ref for the remote-add handling */
-  g_weak_ref_init (&self->repo->sysroot, object);
-
   G_OBJECT_CLASS (ostree_sysroot_parent_class)->constructed (object);
 }
 
@@ -332,21 +325,12 @@ ostree_sysroot_ensure_initialized (OstreeSysroot  *self,
                                cancellable, error))
     return FALSE;
 
-  struct stat stbuf;
-  if (fstatat (self->sysroot_fd, "ostree/repo/objects", &stbuf, 0) != 0)
-    {
-      if (errno != ENOENT)
-        return glnx_throw_errno_prefix (error, "stat(ostree/repo/objects)");
-      else
-        {
-          g_autoptr(GFile) repo_dir = g_file_resolve_relative_path (self->path, "ostree/repo");
-          g_autoptr(OstreeRepo) repo = ostree_repo_new (repo_dir);
-          if (!ostree_repo_create (repo, OSTREE_REPO_MODE_BARE,
-                                   cancellable, error))
-            return FALSE;
-        }
-    }
-
+  g_autoptr(OstreeRepo) repo =
+    ostree_repo_create_at (self->sysroot_fd, "ostree/repo",
+                           OSTREE_REPO_MODE_BARE, NULL,
+                           cancellable, error);
+  if (!repo)
+    return FALSE;
   return TRUE;
 }
 
@@ -772,14 +756,22 @@ ostree_sysroot_load (OstreeSysroot  *self,
 }
 
 static gboolean
-ensure_repo_opened (OstreeSysroot  *self,
-                    GError        **error)
+ensure_repo (OstreeSysroot  *self,
+             GError        **error)
 {
-  if (self->repo_opened)
+  if (self->repo != NULL)
     return TRUE;
-  if (!ostree_repo_open (self->repo, NULL, error))
+  if (!ensure_sysroot_fd (self, error))
+    return FALSE;
+  self->repo = ostree_repo_open_at (self->sysroot_fd, "ostree/repo", NULL, error);
+  if (!self->repo)
     return FALSE;
-  self->repo_opened = TRUE;
+
+  /* Flag it as having been created via ostree_sysroot_get_repo(), and hold a
+   * weak ref for the remote-add handling.
+   */
+  g_weak_ref_init (&self->repo->sysroot, self);
+  self->repo->sysroot_kind = OSTREE_REPO_SYSROOT_KIND_VIA_SYSROOT;
   return TRUE;
 }
 
@@ -796,7 +788,7 @@ ostree_sysroot_load_if_changed (OstreeSysroot  *self,
    * previous to v2017.6, but we do now to support the error-free
    * ostree_sysroot_repo() API.
    */
-  if (!ensure_repo_opened (self, error))
+  if (!ensure_repo (self, error))
     return FALSE;
 
   int bootversion = 0;
@@ -999,9 +991,8 @@ ostree_sysroot_get_repo (OstreeSysroot         *self,
                          GCancellable  *cancellable,
                          GError       **error)
 {
-  if (!ensure_repo_opened (self, error))
+  if (!ensure_repo (self, error))
     return FALSE;
-
   if (out_repo != NULL)
     *out_repo = g_object_ref (self->repo);
   return TRUE;
index dc083754b0fc45760fb4746b6b3e81b2fca54ad4..ba7191ec58e20ff3a6648bc356feda33defb5412 100644 (file)
@@ -70,7 +70,6 @@ static void
 teardown (Fixture       *fixture,
           gconstpointer  test_data)
 {
-  glnx_fd_close int parent_repo_dfd = -1;
   g_autoptr(GError) error = NULL;
 
   /* Recursively remove the temporary directory. */
@@ -81,10 +80,7 @@ teardown (Fixture       *fixture,
 
   /* The repo also needs its source files to be removed. This is the inverse
    * of setup_test_repository() in libtest.sh. */
-  g_autofree gchar *parent_repo_path = g_file_get_path (ostree_repo_get_path (fixture->parent_repo));
-  glnx_opendirat (-1, parent_repo_path, TRUE, &parent_repo_dfd, &error);
-  g_assert_no_error (error);
-
+  int parent_repo_dfd = ostree_repo_get_dfd (fixture->parent_repo);
   glnx_shutil_rm_rf_at (parent_repo_dfd, "../files", NULL, NULL);
   glnx_shutil_rm_rf_at (parent_repo_dfd, "../repo", NULL, NULL);
 
index c84feb527e06694f76c6842af490fbe5dc241a86..f51fb3bb422a40b2e21533d2d63ae5b5a8a0b797 100644 (file)
@@ -73,7 +73,6 @@ static void
 teardown (Fixture       *fixture,
           gconstpointer  test_data)
 {
-  glnx_fd_close int parent_repo_dfd = -1;
   g_autoptr(GError) error = NULL;
 
   /* Recursively remove the temporary directory. */
@@ -84,10 +83,7 @@ teardown (Fixture       *fixture,
 
   /* The repo also needs its source files to be removed. This is the inverse
    * of setup_test_repository() in libtest.sh. */
-  g_autofree gchar *parent_repo_path = g_file_get_path (ostree_repo_get_path (fixture->parent_repo));
-  glnx_opendirat (-1, parent_repo_path, TRUE, &parent_repo_dfd, &error);
-  g_assert_no_error (error);
-
+  int parent_repo_dfd = ostree_repo_get_dfd (fixture->parent_repo);
   glnx_shutil_rm_rf_at (parent_repo_dfd, "../files", NULL, NULL);
   glnx_shutil_rm_rf_at (parent_repo_dfd, "../repo", NULL, NULL);